// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_HTTP_HTTP_TRANSACTION_UNITTEST_H_
#define NET_HTTP_HTTP_TRANSACTION_UNITTEST_H_

#include "net/http/http_transaction.h"

#include <stdint.h>

#include <string>

#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "net/base/io_buffer.h"
#include "net/base/load_flags.h"
#include "net/base/net_error_details.h"
#include "net/base/net_errors.h"
#include "net/base/request_priority.h"
#include "net/base/test_completion_callback.h"
#include "net/disk_cache/disk_cache.h"
#include "net/http/http_cache.h"
#include "net/http/http_request_info.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_response_info.h"
#include "net/log/net_log.h"
#include "net/socket/connection_attempts.h"

namespace net {

class HttpRequestHeaders;
class IOBuffer;
class SSLPrivateKey;
class X509Certificate;
struct HttpRequestInfo;

//-----------------------------------------------------------------------------
// mock transaction data

// these flags may be combined to form the test_mode field
enum {
    TEST_MODE_NORMAL = 0,
    TEST_MODE_SYNC_NET_START = 1 << 0,
    TEST_MODE_SYNC_NET_READ = 1 << 1,
    TEST_MODE_SYNC_CACHE_START = 1 << 2,
    TEST_MODE_SYNC_CACHE_READ = 1 << 3,
    TEST_MODE_SYNC_CACHE_WRITE = 1 << 4,
    TEST_MODE_SYNC_ALL = (TEST_MODE_SYNC_NET_START | TEST_MODE_SYNC_NET_READ | TEST_MODE_SYNC_CACHE_START | TEST_MODE_SYNC_CACHE_READ | TEST_MODE_SYNC_CACHE_WRITE),
    TEST_MODE_SLOW_READ = 1 << 5
};

using MockTransactionReadHandler = int (*)(int64_t content_length,
    int64_t offset,
    IOBuffer* buf,
    int buf_len);
using MockTransactionHandler = void (*)(const HttpRequestInfo* request,
    std::string* response_status,
    std::string* response_headers,
    std::string* response_data);

struct MockTransaction {
    const char* url;
    const char* method;
    // If |request_time| is unspecified, the current time will be used.
    base::Time request_time;
    const char* request_headers;
    int load_flags;
    const char* status;
    const char* response_headers;
    // If |response_time| is unspecified, the current time will be used.
    base::Time response_time;
    const char* data;
    int test_mode;
    MockTransactionHandler handler;
    MockTransactionReadHandler read_handler;
    scoped_refptr<X509Certificate> cert;
    CertStatus cert_status;
    int ssl_connection_status;
    // Value returned by MockNetworkTransaction::Start (potentially
    // asynchronously if |!(test_mode & TEST_MODE_SYNC_NET_START)|.)
    Error return_code;
};

extern const MockTransaction kSimpleGET_Transaction;
extern const MockTransaction kSimplePOST_Transaction;
extern const MockTransaction kTypicalGET_Transaction;
extern const MockTransaction kETagGET_Transaction;
extern const MockTransaction kRangeGET_Transaction;

// returns the mock transaction for the given URL
const MockTransaction* FindMockTransaction(const GURL& url);

// Add/Remove a mock transaction that can be accessed via FindMockTransaction.
// There can be only one MockTransaction associated with a given URL.
void AddMockTransaction(const MockTransaction* trans);
void RemoveMockTransaction(const MockTransaction* trans);

struct ScopedMockTransaction : MockTransaction {
    ScopedMockTransaction()
    {
        AddMockTransaction(this);
    }
    explicit ScopedMockTransaction(const MockTransaction& t)
        : MockTransaction(t)
    {
        AddMockTransaction(this);
    }
    ~ScopedMockTransaction()
    {
        RemoveMockTransaction(this);
    }
};

//-----------------------------------------------------------------------------
// mock http request

class MockHttpRequest : public HttpRequestInfo {
public:
    explicit MockHttpRequest(const MockTransaction& t);
};

//-----------------------------------------------------------------------------
// use this class to test completely consuming a transaction

class TestTransactionConsumer {
public:
    TestTransactionConsumer(RequestPriority priority,
        HttpTransactionFactory* factory);
    virtual ~TestTransactionConsumer();

    void Start(const HttpRequestInfo* request, const BoundNetLog& net_log);

    bool is_done() const { return state_ == DONE; }
    int error() const { return error_; }

    const HttpResponseInfo* response_info() const
    {
        return trans_->GetResponseInfo();
    }
    const HttpTransaction* transaction() const { return trans_.get(); }
    const std::string& content() const { return content_; }

private:
    enum State {
        IDLE,
        STARTING,
        READING,
        DONE
    };

    void DidStart(int result);
    void DidRead(int result);
    void DidFinish(int result);
    void Read();

    void OnIOComplete(int result);

    State state_;
    std::unique_ptr<HttpTransaction> trans_;
    std::string content_;
    scoped_refptr<IOBuffer> read_buf_;
    int error_;

    static int quit_counter_;
};

//-----------------------------------------------------------------------------
// mock network layer

class MockNetworkLayer;

// This transaction class inspects the available set of mock transactions to
// find data for the request URL.  It supports IO operations that complete
// synchronously or asynchronously to help exercise different code paths in the
// HttpCache implementation.
class MockNetworkTransaction
    : public HttpTransaction,
      public base::SupportsWeakPtr<MockNetworkTransaction> {
    typedef WebSocketHandshakeStreamBase::CreateHelper CreateHelper;

public:
    MockNetworkTransaction(RequestPriority priority, MockNetworkLayer* factory);
    ~MockNetworkTransaction() override;

    int Start(const HttpRequestInfo* request,
        const CompletionCallback& callback,
        const BoundNetLog& net_log) override;

    int RestartIgnoringLastError(const CompletionCallback& callback) override;

    int RestartWithCertificate(X509Certificate* client_cert,
        SSLPrivateKey* client_private_key,
        const CompletionCallback& callback) override;

    int RestartWithAuth(const AuthCredentials& credentials,
        const CompletionCallback& callback) override;

    bool IsReadyToRestartForAuth() override;

    int Read(IOBuffer* buf,
        int buf_len,
        const CompletionCallback& callback) override;
    void PopulateNetErrorDetails(NetErrorDetails* details) const override;

    void StopCaching() override;

    bool GetFullRequestHeaders(HttpRequestHeaders* headers) const override;

    int64_t GetTotalReceivedBytes() const override;

    int64_t GetTotalSentBytes() const override;

    void DoneReading() override;

    const HttpResponseInfo* GetResponseInfo() const override;

    LoadState GetLoadState() const override;

    UploadProgress GetUploadProgress() const override;

    void SetQuicServerInfo(QuicServerInfo* quic_server_info) override;

    bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const override;

    bool GetRemoteEndpoint(IPEndPoint* endpoint) const override;

    void SetPriority(RequestPriority priority) override;

    void SetWebSocketHandshakeStreamCreateHelper(
        CreateHelper* create_helper) override;

    void SetBeforeNetworkStartCallback(
        const BeforeNetworkStartCallback& callback) override;

    void SetBeforeHeadersSentCallback(
        const BeforeHeadersSentCallback& callback) override;

    int ResumeNetworkStart() override;

    void GetConnectionAttempts(ConnectionAttempts* out) const override;

    CreateHelper* websocket_handshake_stream_create_helper()
    {
        return websocket_handshake_stream_create_helper_;
    }
    RequestPriority priority() const { return priority_; }
    const HttpRequestInfo* request() const { return request_; }

    // Bogus value that will be returned by GetTotalReceivedBytes() if the
    // MockNetworkTransaction was started.
    static const int64_t kTotalReceivedBytes;
    // Bogus value that will be returned by GetTotalSentBytes() if the
    // MockNetworkTransaction was started.
    static const int64_t kTotalSentBytes;

private:
    int StartInternal(const HttpRequestInfo* request,
        const CompletionCallback& callback,
        const BoundNetLog& net_log);
    void CallbackLater(const CompletionCallback& callback, int result);
    void RunCallback(const CompletionCallback& callback, int result);

    const HttpRequestInfo* request_;
    HttpResponseInfo response_;
    std::string data_;
    int64_t data_cursor_;
    int64_t content_length_;
    int test_mode_;
    RequestPriority priority_;
    MockTransactionReadHandler read_handler_;
    CreateHelper* websocket_handshake_stream_create_helper_;
    BeforeNetworkStartCallback before_network_start_callback_;
    base::WeakPtr<MockNetworkLayer> transaction_factory_;
    int64_t received_bytes_;
    int64_t sent_bytes_;

    // NetLog ID of the fake / non-existent underlying socket used by the
    // connection. Requires Start() be passed a BoundNetLog with a real NetLog to
    // be initialized.
    unsigned int socket_log_id_;

    bool done_reading_called_;

    base::WeakPtrFactory<MockNetworkTransaction> weak_factory_;
};

class MockNetworkLayer : public HttpTransactionFactory,
                         public base::SupportsWeakPtr<MockNetworkLayer> {
public:
    MockNetworkLayer();
    ~MockNetworkLayer() override;

    int transaction_count() const { return transaction_count_; }
    bool done_reading_called() const { return done_reading_called_; }
    bool stop_caching_called() const { return stop_caching_called_; }
    void TransactionDoneReading();
    void TransactionStopCaching();

    // Resets the transaction count. Can be called after test setup in order to
    // make test expectations independent of how test setup is performed.
    void ResetTransactionCount();

    // Returns the last priority passed to CreateTransaction, or
    // DEFAULT_PRIORITY if it hasn't been called yet.
    RequestPriority last_create_transaction_priority() const
    {
        return last_create_transaction_priority_;
    }

    // Returns the last transaction created by
    // CreateTransaction. Returns a NULL WeakPtr if one has not been
    // created yet, or the last transaction has been destroyed, or
    // ClearLastTransaction() has been called and a new transaction
    // hasn't been created yet.
    base::WeakPtr<MockNetworkTransaction> last_transaction()
    {
        return last_transaction_;
    }

    // Makes last_transaction() return NULL until the next transaction
    // is created.
    void ClearLastTransaction()
    {
        last_transaction_.reset();
    }

    // HttpTransactionFactory:
    int CreateTransaction(RequestPriority priority,
        std::unique_ptr<HttpTransaction>* trans) override;
    HttpCache* GetCache() override;
    HttpNetworkSession* GetSession() override;

    // The caller must guarantee that |clock| will outlive this object.
    void SetClock(base::Clock* clock);
    base::Clock* clock() const { return clock_; }

    // The current time (will use clock_ if it is non NULL).
    base::Time Now();

private:
    int transaction_count_;
    bool done_reading_called_;
    bool stop_caching_called_;
    RequestPriority last_create_transaction_priority_;

    // By default clock_ is NULL but it can be set to a custom clock by test
    // frameworks using SetClock.
    base::Clock* clock_;

    base::WeakPtr<MockNetworkTransaction> last_transaction_;
};

//-----------------------------------------------------------------------------
// helpers

// read the transaction completely
int ReadTransaction(HttpTransaction* trans, std::string* result);

} // namespace net

#endif // NET_HTTP_HTTP_TRANSACTION_UNITTEST_H_
